const crypto = require("crypto");
const dm = require("../core/DataManager");
const jwt = require('jsonwebtoken'); //用来创建和确认用户信息摘要

const ltrt = NCUTCAT.localProperty.login_time_range_threshold;
const ltp = NCUTCAT.localProperty.login_token_private;
const ltt = `${NCUTCAT.localProperty.login_token_time}m`;

const permissionID = {
    'check_user': '查看用户信息',
    'check_content': '审核内容',
    'release_notice': '发布公告',
    'create_cat': '创建猫咪',
    'see_private_notice': '查看未公开的公告',
    'update_cat': '更新猫咪',
    'remove_cat': '删除猫咪',
    'see_private_cat_img': '查看未公开的猫咪图片',
    'create_invite_code': '创建邀请码',
    'see_invite_code': '查看邀请码',
    'see_private_thread_img': '查看未公开的帖子图片',
    'edit_other_thread': '编辑他人帖子'
};


(() => {
    const fields = ['p_a', 'p_b', 'p_c', 'p_d'],
        ks = Object.keys(permissionID),
        l = ks.length,
        size = 31;
    let i = 0,
        j = 0;
    while (i < l) {
        let k = ks[i];
        let name = permissionID[k];
        let code = 1 << (i++ % size);
        permissionID[k] = { code: code, name: name, field: fields[j] };
        (i % size) || ++j;
    }
    // console.log(permissionID);
})();

function DEBUG_printPermissionID(argument) {
    for (ks = Object.keys(permissionID), i = 0, l = ks.length; i < l; i++) {
        let k = ks[i];
        let name = permissionID[k].name;
        let code = permissionID[k].code;
        let field = permissionID[k].field;
        console.log(`${code}: ${k} ${name} (${field}, ${Math.log2(code)}) `);
    }
}
// DEBUG_printPermissionID();

/**
 * 获取用户所拥有的权限
 * @param user 用户域(从数据库提取)
 * @return {权限名: 权限解释, ... }
 */
module.exports.getPermissions = (user) => {
    let ps = {};
    if (user)
        for (let name in permissionID) {
            let permission = permissionID[name];
            if (user[permission.field] & permission.code) ps[name] = permission.name;
        }
    return ps;
}
/**
 * 权限检查
 * 当尝试使用权限时, 检查用户权限码是否拥有对应的权限
 * @param usePermission 是否尝试使用权限, 若此内容为false, 则必定返回false
 * @param user 用户信息
 * @param type 权限类型, 详见 permissionID
 */
module.exports.permissionCheck = function(usePermission, user, type) {
    if (!usePermission || !user) return false;
    let p = permissionID[type];
    if (!p) return false;
    return user[p.field] & p.code;
}
/**
 * 检查器
 * 用户必须通过登录才能访问
 */
module.exports.middle_checker = function(req, res, next) {
    module.exports.checkUserByReq(req, (err, result) => {
        if (err) res.status(403).json({
            success: false,
            message: err
        });
        else {
            req.token_result = result;
            next();
        }
    })
}
/**
 * 寻找器
 * 用户不登录也可访问, 但若登录可获得更多信息
 */
module.exports.middle_finder = function(req, res, next) {
    module.exports.checkUserByReq(req, (err, result) => {
        if (!err) req.token_result = result;
        next();
    })
}


const ugct = NCUTCAT.localProperty.userManager_getUserByID_cache_threshold;
/**用户缓存, 减少查询*/
const userCache = {
    getUserByID: async (uid, cb) => {
        if (userCache[uid]) return userCache[uid].time = ugct, cb(null, userCache[uid].user);
        dm.getUserByID(uid, (err, user) => {
            if (user) userCache[uid] = { user: user, time: ugct };
            cb(err, user);
        });
    }
};
setInterval(() => {
    for (let k in userCache)
        if (userCache[k].time) --userCache[k].time <= 0 && delete userCache[k];
}, 1000);

/**
 * 尝试加载用户信息
 * 当用户登录后(需要前置模块 middle_finder/middle_checker)可以从数据库加载用户信息
 */
module.exports.middle_loadUser = function(req, res, next) {
    let token = req.token_result || {};
    let uid = token.uid || "";
    if (uid) {
        userCache.getUserByID(uid, (err, user) => {
            if (err || !user) {
                NCUTCAT.warning(err, user);
                return res.json(RES_CHECKER({ success: false, err: "查找用户时出错或用户不存在" }));
            }
            req.user = user;
            next();
        })
    } else next();
}

/**
 * 检查用户登录情况
 * 当用户token存在且合法时cb的err为"", result为解码结果
 * 反之err为错误信息, result为""
 * @param req 用户请求
 * @param cb callback(err,result)
 */
module.exports.checkUserByReq = function(req, cb) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'] || getCookie(req.headers.cookie, 'login-token');
    module.exports.checkUser(token, cb);
}
/**
 * 检查用户登录情况
 * 当用户token存在且合法时cb的err为"", result为解码结果
 * 反之err为错误信息, result为""
 * @param req 用户请求
 * @param cb callback(err,result)
 */
module.exports.checkUser = function(token, cb) {
    if (!cb) return; //?
    if (token) jwt.verify(token, ltp, (err, result) => err ? cb("无效token", "") : cb("", result));
    else cb("未登录", "");
}

/**
 * 颁发token
 * 通过 sha256(sha256(原始密码)+验证时间戳) 得出pwd_sha
 * 验证时间戳必须和提交到服务器验证时的时间戳接近, 相差不得超过 NCUTCAT.localProperty.login_time_range_threshold 阈值
 * @param name 用户名
 * @param pwd_sha 密码的sha加密
 * @param time 计算时间戳
 * @param cb 回调函数 cb(err,token)
 */
module.exports.getToken = async function(name, pwd_sha, time, cb) {
    let now = new Date().getTime();
    if (Math.abs(now - parseInt(time)) > ltrt) return cb("时间戳验证不通过", null);

    dm.getUser(name, (err, user) => {
        if (err) return cb(err, null);
        if (user) {
            let sha = encryption(user.pwd + time);
            if (sha === pwd_sha) {
                let token = jwt.sign({ time: now, uid: user.uid }, ltp, {
                    expiresIn: ltt // 设置过期时间
                });
                cb("", token);
            } else cb("用户名不存在或密码不正确", null);
        } else {
            cb("用户名不存在或密码不正确", null);
        }
    });
}

/**加密算法*/
function encryption(pwd) {
    var encoder = crypto.createHash('sha256'); //md5, sha1, sha256
    return encoder.update(pwd).digest('base64');
}

/**
 * 获取cookie
 * @param cookies 全部cookie
 * @param name 要寻找的cookie
 * @return 值
 */
function getCookie(cookies, name) {
    if (typeof cookies !== 'string') return;
    cookies = cookies.split(';');
    for (let c in cookies) {
        c = cookies[c].trim();
        if (c.startsWith(name)) return c.substring(name.length + 1);
    }
}

NCUTCAT.infoLog("UserManager", "成功加载");